package com.extremelylargepackage.sprite;

import java.util.ArrayList;
import java.util.List;

import android.graphics.Bitmap;
import android.graphics.Canvas;

/**
 * The Animation class manages a series of images (frames) and the amount of
 * time to display each frame.
 */
public class Animation {
	private List<AnimFrame> _frames;

	private int _currFrameIndex;
	private long _animTime;
	private long _totalDuration;
	private boolean _repeat;
	private boolean _centered;

	/**
	 * Width and height of the animation. Each one is independently based on the
	 * frame with the greatest width and height, respectively.
	 */
	private float _width, _height;

	private String _name;

	/**
	 * Creates a new, empty Animation.
	 */
	public Animation(String name)
	{
		this(name, new ArrayList<AnimFrame>(), 0, false, true);
	}

	private Animation(String name, List<AnimFrame> frames, long totalDuration, boolean repeat, boolean centered)
	{
		_name = name;
		_frames = frames;
		_totalDuration = totalDuration;
		_repeat = repeat;
		_centered = centered;

		_width = _height = 0;
		for(AnimFrame frame : frames) {
			if(frame._sprite.getWidth() > _width)
				_width = frame._sprite.getWidth();
			if(frame._sprite.getHeight() > _height)
				_height = frame._sprite.getWidth();
		}

		start();
	}

	/**
	 * Creates a duplicate of this animation. The list of frames are shared
	 * between the two Animations, but each Animation can be animated
	 * independently.
	 */
	@Override
	public Object clone()
	{
		return new Animation(_name, _frames, _totalDuration, _repeat, _centered);
	}

	/**
	 * Adds an image to the animation with the specified duration (time to
	 * display the image).
	 */
	public synchronized void addFrame(AbstractSprite sprite, long duration)
	{
		_totalDuration += duration;
		_frames.add(new AnimFrame(sprite, _totalDuration));

		if(sprite.getWidth() > _width)
			_width = sprite.getWidth();
		if(sprite.getHeight() > _height)
			_height = sprite.getHeight();
	}

	/**
	 * Starts this animation over from the beginning.
	 */
	public synchronized void start()
	{
		_animTime = 0;
		_currFrameIndex = 0;
	}

	/**
	 * Updates this animation's current image (frame), if necessary.
	 */
	public synchronized void update(long elapsedTime)
	{
		if (_frames.size() > 1) {
			_animTime += elapsedTime;

			if (_animTime >= _totalDuration) {
				if(_repeat) {
					_animTime = _animTime % _totalDuration;
					_currFrameIndex = 0;
				} else {
					_animTime = _totalDuration-1; // stay right before the end
				}
			}

			while (_animTime > getFrame(_currFrameIndex)._endTime) {
				_currFrameIndex++;
			}
		}
	}
	
	public void draw(Canvas c, float x, float y)
	{
		Bitmap currentFrame = getBitmap();
		if(_centered) {
			x += (_width - currentFrame.getWidth()) / 2;
			y += (_height - currentFrame.getHeight()) / 2;
		}

		c.drawBitmap(currentFrame, x, y, null);
	}

	public boolean finished()
	{
		return !_repeat && _animTime == _totalDuration - 1;
	}

	/**
	 * Gets this Animation's current image. Returns null if this animation has
	 * no images.
	 */
	public synchronized Bitmap getBitmap()
	{
		if (_frames.size() == 0) {
			return null;
		} else {
			return getFrame(_currFrameIndex)._sprite.getBitmap();
		}
	}

	private AnimFrame getFrame(int i)
	{
		return _frames.get(i);
	}

	public float getWidth()
	{
		return _width;
	}

	public float getHeight()
	{
		return _height;
	}

	public void setRepeat(boolean repeat)
	{
		_repeat = repeat;
	}

	public void setCentered(boolean centered)
	{
		_centered = centered;
	}

	private class AnimFrame {
		AbstractSprite _sprite;
		long _endTime;

		public AnimFrame(AbstractSprite image, long endTime)
		{
			this._sprite = image;
			this._endTime = endTime;
		}
	}

	public String getName()
	{
		return _name;
	}
}
